home *** CD-ROM | disk | FTP | other *** search
/ Linux Cubed Series 4: GNU Archives / Linux Cubed Series 4 - GNU Archives.iso / gnu / enscript.4 / enscript / enscript-1.4.0 / src / psgen.c < prev    next >
Encoding:
C/C++ Source or Header  |  1996-06-30  |  45.4 KB  |  2,117 lines

  1. /* 
  2.  * Convert ASCII to PostScript.
  3.  * Copyright (c) 1995 Markku Rossi.
  4.  *
  5.  * Author: Markku Rossi <mtr@iki.fi>
  6.  */
  7.  
  8. /*
  9.  * This file is part of GNU enscript.
  10.  * 
  11.  * This program is free software; you can redistribute it and/or modify
  12.  * it under the terms of the GNU General Public License as published by
  13.  * the Free Software Foundation; either version 2, or (at your option)
  14.  * any later version.
  15.  *
  16.  * This program is distributed in the hope that it will be useful,
  17.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  18.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  19.  * GNU General Public License for more details.
  20.  *
  21.  * You should have received a copy of the GNU General Public License
  22.  * along with this program; see the file COPYING.  If not, write to
  23.  * the Free Software Foundation, 59 Temple Place - Suite 330,
  24.  * Boston, MA 02111-1307, USA.
  25.  */
  26.  
  27. #include "gsint.h"
  28.  
  29. /* 
  30.  * Types and definitions.
  31.  */
  32.  
  33. /* Values for token flags. */
  34.  
  35. /* EPSF. */
  36. #define F_EPSF_CENTER            0x01
  37. #define F_EPSF_RIGHT            0x02
  38. #define M_EPSF_JUSTIFICATION        0x03
  39.  
  40. #define F_EPSF_NO_CPOINT_UPDATE_X    0x04
  41. #define F_EPSF_NO_CPOINT_UPDATE_Y    0x08
  42.  
  43. #define F_EPSF_ABSOLUTE_X        0x10
  44. #define F_EPSF_ABSOLUTE_Y        0x20
  45.  
  46. #define F_EPSF_SCALE_X            0x40
  47. #define F_EPSF_SCALE_Y            0x80
  48.  
  49.  
  50. /* Predicates for the current body font. */
  51.  
  52. /* Is character <ch> printable. */
  53. #define ISPRINT(ch) (font_ctype[(unsigned char) (ch)] != ' ')
  54.  
  55. /* Does character <ch> exist in current body font? */
  56. #define EXISTS(ch) (font_ctype[(unsigned char) (ch)] == '*')
  57.  
  58.  
  59. #define RESOURCE_LINE_WIDTH 75
  60.  
  61. /* Token types. */
  62. typedef enum
  63. {
  64.   tNONE,
  65.   tEOF,
  66.   tSTRING,
  67.   tFORMFEED,
  68.   tNEWLINE,
  69.   tCARRIAGE_RETURN,
  70.   tWRAPPED_NEWLINE,
  71.   tEPSF,
  72.   tSETFILENAME,
  73.   tSETPAGENUMBER,
  74.   tNEWPAGE,
  75.   tFONT,
  76.   tPS
  77. } TokenType;
  78.  
  79. /* Special escape tokens. */
  80. typedef enum
  81. {
  82.   ESC_COMMENT,
  83.   ESC_EPSF,
  84.   ESC_FONT,
  85.   ESC_NEWPAGE,
  86.   ESC_SETFILENAME,
  87.   ESC_SETPAGENUMBER,
  88.   ESC_SHADE,
  89.   ESC_PS
  90. } SpecialEscape;
  91.  
  92. /* Token structure. */
  93. struct gs_token_st
  94. {
  95.   TokenType type;
  96.   unsigned int flags;
  97.   double new_x;            /* Current point x after this token. */
  98.   double new_y;            /* Current point y after this token. */
  99.   int new_col;            /* Line column after this token. */
  100.  
  101.   union
  102.     {
  103.       int i;
  104.       char *str;
  105.       struct
  106.     {
  107.       double x;        /* x-offset */
  108.       double y;        /* y-offset */
  109.       double w;        /* width */
  110.       double h;        /* height */
  111.       double xscale;
  112.       double yscale;
  113.       int llx, lly, urx, ury; /* Bounding box. */
  114.       char filename[512];
  115.       char *skipbuf;
  116.       unsigned int skipbuf_len;
  117.       unsigned int skipbuf_pos;
  118.       FILE *fp;        /* File from which eps image is read. */
  119.       int pipe;        /* Is <fp> opened to pipe?  */
  120.     } epsf;
  121.       struct
  122.     {
  123.       char name[512];
  124.       double size;
  125.     } font;
  126.       char filename[512];
  127.     } u;
  128. };
  129.  
  130. typedef struct gs_token_st Token;
  131.  
  132.  
  133. /*
  134.  * Prototypes for static functions.
  135.  */
  136.  
  137. static void get_next_token __P ((InputStream *is, double linepos,
  138.                  unsigned int col, double linew,
  139.                  Token *token));
  140.  
  141. static void dump_ps_page_header __P ((char *fname));
  142.  
  143. static void dump_ps_page_trailer ();
  144.  
  145. /*
  146.  * Recognize a EPS file described by <token>.  Returns 1 if file was a
  147.  * valid EPS file or 0 otherwise.  File is accepted if it starts with
  148.  * the PostScript magic `%!' and it has a valid `%%BoundingBox' DSC
  149.  * comment.
  150.  */
  151. static int recognize_eps_file __P ((Token *token));
  152.  
  153. /*
  154.  * Insert EPS file described by <token> to the output stream.
  155.  */
  156. static void paste_epsf __P ((Token *token));
  157.  
  158. /*
  159.  * Check if InputStream <is> contains a file which can be passed
  160.  * through without any modifications.  Returns 1 if file was passed or
  161.  * 0 otherwise.
  162.  */
  163. static int do_pass_through __P ((char *fname, InputStream *is));
  164.  
  165. /*
  166.  * Read one float dimension from InputStream <is>.  If <units> is
  167.  * true, number can be followed by an optional unit specifier.  If
  168.  * <horizontal> is true, dimension is horizontal, otherwise it is
  169.  * vertical (this is used to find out how big `line' units are).
  170.  */
  171. static double read_float __P ((InputStream *is, int units, int horizontal));
  172.  
  173. /*
  174.  * Print linenumber <linenum> to the beginning of the current line.
  175.  * Current line start is specified by point (x, y).
  176.  */
  177. static void print_line_number __P ((double x, double y, double space,
  178.                     double margin, unsigned int linenum));
  179.  
  180. /* Send PostScript to the output file. */
  181. static void output __P ((char *fmt, ...));
  182.  
  183. /* Divert output to tmp file so the total page count can be counted. */
  184. static void divert ();
  185.  
  186. /* Paste diverted data to the output and patch the total page counts. */
  187. static void undivert ();
  188.  
  189.  
  190. /*
  191.  * Global variables.
  192.  */
  193.  
  194. /* The number of the current page. */
  195. unsigned int current_pagenum;
  196. unsigned int total_pages_in_file;
  197.  
  198.  
  199. /*
  200.  * Static variables
  201.  */
  202.  
  203. /* Have we dumped PS header? */
  204. static int ps_header_dumped = 0;
  205.  
  206. /* Divert file. */
  207. static FILE *divertfp = NULL;
  208.  
  209. /* Current output() file. */
  210. static FILE *cofp = NULL;
  211.  
  212. /* To print or not to print, that's a question. */
  213. static int do_print = 1;
  214.  
  215. /* Is ^@font{}-defined font active? */
  216. static int user_font = 0;
  217.  
  218. /* The user ^@font{}-defined font. */
  219. static char user_font_name[256];
  220. static double user_font_pt;
  221.  
  222. /* The last linenumber printed by print_line_number(). */
  223. static unsigned int print_line_number_last;
  224.  
  225. /*
  226.  * Global functions.
  227.  */
  228.  
  229. void
  230. dump_ps_header ()
  231. {
  232.   char *cp, *cp2;
  233.   int i, j, got;
  234.  
  235.   /* Dump PS header only once. */
  236.   if (ps_header_dumped)
  237.     return;
  238.   ps_header_dumped = 1;
  239.  
  240.   /* 
  241.    * Header.
  242.    */
  243.  
  244.   output ("%%!PS-Adobe-3.0\n");
  245.   output ("%%%%BoundingBox: %d %d %d %d\n", media->llx, media->lly,
  246.       media->urx, media->ury);
  247.   output ("%%%%Title: %s\n", title);
  248.   output ("%%%%For: %s\n", passwd->pw_gecos);
  249.   output ("%%%%Creator: %s\n", version_string);
  250.   output ("%%%%CreationDate: %s\n", date_string);
  251.   output ("%%%%Orientation: %s\n", landscape ? "Landscape" : "Portrait");
  252.   output ("%%%%Pages: (atend)\n");
  253.   output ("%%%%DocumentMedia: %s %d %d 0 () ()\n",
  254.       media->name, media->w, media->h);
  255.   output ("%%%%DocumentNeededResources: (atend)\n");
  256.  
  257.   if (count_key_value_set (pagedevice) > 0)
  258.     output ("%%%%LanguageLevel: 2\n");
  259.  
  260.   output ("%%%%EndComments\n");
  261.  
  262.  
  263.   /*
  264.    * Procedure Definitions.
  265.    */
  266.  
  267.   output ("%%%%BeginProlog\n");
  268.  
  269.   /* Prolog. */
  270.   output ("%%%%BeginResource: procset Enscript-Prolog %s\n",
  271.       ps_version_string);
  272.   if (!paste_file ("enscript", ".pro"))
  273.     fatal (_("couldn't find prolog \"%s\": %s\n"), "enscript.pro",
  274.        strerror (errno));
  275.   output ("%%%%EndResource\n");
  276.  
  277.   /* Encoding vector. */
  278.   output ("%%%%BeginResource: procset Enscript-Encoding-%s %s\n",
  279.       encoding_name, ps_version_string);
  280.   if (!paste_file (encoding_name, ".enc"))
  281.     fatal (_("couldn't find encoding file \"%s.enc\": %s\n"), encoding_name,
  282.        strerror (errno));
  283.   output ("%%%%EndResource\n");
  284.  
  285.   output ("%%%%EndProlog\n");
  286.  
  287.  
  288.   /* 
  289.    * Document Setup.
  290.    */
  291.  
  292.   output ("%%%%BeginSetup\n");
  293.  
  294.   /* Download fonts. */
  295.   for (got = strhash_get_first (download_fonts, &cp, &j, (void **) &cp2); got;
  296.        got = strhash_get_next (download_fonts, &cp, &j, (void **) &cp2))
  297.     download_font (cp);
  298.  
  299.   /* For each required font, emit %%IncludeResouce comment. */
  300.   for (got = strhash_get_first (res_fonts, &cp, &j, (void **) &cp2); got;
  301.        got = strhash_get_next (res_fonts, &cp, &j, (void **) &cp2))
  302.     output ("%%%%IncludeResource: font %s\n", cp);
  303.  
  304.   /* User supplied header? */
  305.   if (page_header)
  306.     {
  307.       output ("/user_header_p true def\n");
  308.       output ("/user_header_str (%s) def\n", page_header);
  309.     }
  310.   else
  311.     output ("/user_header_p false def\n");
  312.  
  313.   output ("/HFpt %g def\n", HFpt);
  314.  
  315.  
  316.   /* Select our fonts. */
  317.  
  318.   /* Header font HF. */
  319.   output ("/%s /HF-gs-font MF\n", HFname);
  320.   output ("/HF /HF-gs-font findfont HFpt scalefont def\n");
  321.  
  322.   /* Our default typing font F. */
  323.   output ("/%s /F-gs-font MF\n", Fname);
  324.   output ("/F-gs-font findfont %g scalefont setfont\n", Fpt);
  325.  
  326.   /* Underlay. */
  327.   if (underlay != NULL)
  328.     {
  329.       output ("/ul_str (%s) def\n", underlay);
  330.       output ("/ul_ptsize %g def\n", ul_ptsize);
  331.       output ("/ul_gray %g def\n", ul_gray);
  332.       output ("/ul_x %g def\n", ul_x);
  333.       output ("/ul_y %g def\n", ul_y);
  334.       output ("/ul_angle %g def\n", ul_angle);
  335.       output ("/ul_style %d def\n", ul_style);
  336.       output ("/%s /F-ul-font MF\n", ul_font);
  337.       output ("/ul_font /F-ul-font findfont ul_ptsize scalefont def\n");
  338.     }
  339.  
  340.   /* Number of copies. */
  341.   output ("/#copies %d def\n", num_copies);
  342.  
  343.   /* Page prefeed. */
  344.   if (page_prefeed)
  345.     output ("true page_prefeed\n");
  346.  
  347.   /* Statusdict definitions. */
  348.   if (count_key_value_set (statusdict) > 0)
  349.     {
  350.       output ("%% Statustdict definitions:\nstatusdict begin\n  ");
  351.       i = 2;
  352.       for (got = strhash_get_first (statusdict, &cp, &j, (void **) &cp2); got;
  353.        got = strhash_get_next (statusdict, &cp, &j, (void **) &cp2))
  354.     {
  355.       j = strlen (cp) + 1 + strlen (cp2) + 1;
  356.       if (i + j > RESOURCE_LINE_WIDTH)
  357.         {
  358.           output ("\n  ");
  359.           i = 2;
  360.         }
  361.       output ("%s %s ", cp2, cp);
  362.       i += j;
  363.     }
  364.       output ("\nend\n");
  365.     }
  366.  
  367.   /* Page device definitions. */
  368.   if (count_key_value_set (pagedevice) > 0)
  369.     {
  370.       output ("%% Pagedevice definitions:\n");
  371.       output ("gs_languagelevel 1 gt {\n  <<\n    ");
  372.       i = 4;
  373.       for (got = strhash_get_first (pagedevice, &cp, &j, (void **) &cp2); got;
  374.        got = strhash_get_next (pagedevice, &cp, &j, (void **) &cp2))
  375.     {
  376.       j = strlen (cp2) + 1 + strlen (cp) + 2;
  377.       if (i + j > RESOURCE_LINE_WIDTH)
  378.         {
  379.           output ("\n    ");
  380.           i = 4;
  381.         }
  382.       output ("/%s %s ", cp, cp2);
  383.       i += j;
  384.     }
  385.       output ("\n  >> setpagedevice\n} if\n");
  386.     }
  387.  
  388.   /*
  389.    * Dump header procset.  Header must come after all font inclusions
  390.    * and enscript's dynamic state definition.
  391.    */
  392.   if (header != HDR_NONE)
  393.     {
  394.       char *hdr;
  395.       if (header == HDR_SIMPLE)
  396.     hdr = "simple";
  397.       else
  398.     hdr = fancy_header_name;
  399.       
  400.       output ("%%%%BeginResource: procset Enscript-Header-%s %s\n",
  401.           hdr, ps_version_string);
  402.       if (!paste_file (hdr, ".hdr"))
  403.     fatal (_("couldn't find header definition file \"%s.hdr\": %s\n"), hdr,
  404.            strerror (errno));
  405.       output ("%%%%EndResource\n");
  406.     }
  407.  
  408.   /*
  409.    * Count output width and height here; we can't do it earlier because
  410.    * header might have just allocated some extra space.
  411.    */
  412.   d_output_w = d_page_w;
  413.   d_output_h = d_page_h - d_header_h - d_footer_h;
  414.  
  415.   /* Dump our current dynamic state. */
  416.   output ("/d_page_w %d def\n", d_page_w);
  417.   output ("/d_page_h %d def\n", d_page_h);
  418.  
  419.   output ("/d_header_x %d def\n", 0);
  420.   output ("/d_header_y %d def\n", d_output_h + d_footer_h);
  421.   output ("/d_header_w %d def\n", d_header_w);
  422.   output ("/d_header_h %d def\n", d_header_h);
  423.  
  424.   output ("/d_footer_x %d def\n", 0);
  425.   output ("/d_footer_y %d def\n", 0);
  426.   output ("/d_footer_w %d def\n", d_header_w);
  427.   output ("/d_footer_h %d def\n", d_footer_h);
  428.  
  429.   output ("/d_output_w %d def\n", d_output_w);
  430.   output ("/d_output_h %d def\n", d_output_h);
  431.   output ("/cols %d def\n", num_columns);
  432.  
  433.   output ("%%%%EndSetup\n");
  434. }
  435.  
  436.  
  437. void
  438. dump_ps_trailer ()
  439. {
  440.   int i, j, got;
  441.   char *cp;
  442.   void *value;
  443.  
  444.   if (!ps_header_dumped)
  445.     /* No header, let's be consistent and forget trailer also. */
  446.     return;
  447.  
  448.   /* Trailer. */
  449.  
  450.   output ("%%%%Trailer\n");
  451.  
  452.   if (page_prefeed)
  453.     output ("false page_prefeed\n");
  454.  
  455.   output ("%%%%Pages: %d\n", total_pages);
  456.  
  457.   /* Document needed resources. */
  458.  
  459.   /* fonts. */
  460.   output ("%%%%DocumentNeededResources: font ");
  461.   i = 32;            /* length of the previous string. */
  462.   for (got = strhash_get_first (res_fonts, &cp, &j, &value); got;
  463.        got = strhash_get_next (res_fonts, &cp, &j, &value))
  464.     {
  465.       if (i + strlen (cp) + 1 > RESOURCE_LINE_WIDTH)
  466.     {
  467.       output ("\n%%%%+ font ");
  468.       i = 9;        /* length of the previous string. */
  469.     }
  470.       output ("%s ", cp);
  471.       i += strlen (cp) + 1;
  472.     }
  473.   output ("\n");
  474.  
  475.   output ("%%%%EOF\n");
  476. }
  477.  
  478.  
  479. void
  480. process_file (char *fname_arg, InputStream *is)
  481. {
  482.   int col;
  483.   double x, y;
  484.   double lx, ly;
  485.   double linewidth;        /* Line width in points. */
  486.   double lineend;
  487.   int done = 0;
  488.   int page_clear = 1;
  489.   unsigned int line_column;
  490.   unsigned int current_linenum;
  491.   unsigned int current_file_linenum = 1;
  492.   double linenumber_space = 0;
  493.   double linenumber_margin = 0;
  494.   Token token;
  495.   int reuse_last_token = 0;
  496.   char fname[512];
  497.  
  498.   /* Save filename. */
  499.   strcpy (fname, fname_arg);
  500.  
  501.   /* Init page number count. */
  502.   current_pagenum = 0;
  503.   total_pages_in_file = 0;
  504.  
  505.   /* We haven't printed any line numbers yet. */
  506.   print_line_number_last = (unsigned int) -1;
  507.  
  508.   if (pass_through && do_pass_through (fname, is))
  509.     return;
  510.  
  511.   /* We have work to do, let's give header a chance to dump itself. */
  512.   dump_ps_header ();
  513.  
  514.   message (1, _("processing file \"%s\"...\n"), fname);
  515.  
  516.   linewidth = d_output_w / num_columns - 2 * d_output_x_margin
  517.     - line_indent;
  518.  
  519.   divert ();
  520.  
  521.   while (!done)
  522.     {
  523.       /* Start a new page. */
  524.       page_clear = 1;
  525.  
  526.       for (col = 0; !done && col < num_columns; col++)
  527.     {
  528.       /* Move to the beginning of the column <col>. */
  529.       lx = x = col * d_output_w / (float) num_columns + d_output_x_margin
  530.         + line_indent;
  531.       lineend = lx + linewidth;
  532.  
  533.       ly = y = d_footer_h + d_output_h - d_output_y_margin - LINESKIP;
  534.       current_linenum = 0;
  535.       line_column = 0;
  536.  
  537.       while (1)
  538.         {
  539.           if (line_numbers && line_column == 0)
  540.         {
  541.           /* 
  542.            * Count the space needed by linenumbers.  This should
  543.            * be enought for 99999 lines.
  544.            */
  545.  
  546.           linenumber_space = CHAR_WIDTH ('0') * 5 + 1.0;
  547.           linenumber_margin = CHAR_WIDTH (':') + CHAR_WIDTH ('m');
  548.  
  549.           /* Forward x by the amount needed by our line numbers. */
  550.           x += linenumber_space + linenumber_margin;
  551.         }
  552.  
  553.           /* Get token. */
  554.           if (!reuse_last_token)
  555.         get_next_token (is, x, line_column, lineend, &token);
  556.           reuse_last_token = 0;
  557.  
  558.           /*
  559.            * Page header printing is delayed to this point because
  560.            * we want to handle files ending with a newline character
  561.            * with care.  If the last newline would cause a pagebreak,
  562.            * otherwise we would print page header to the non-existent
  563.            * next page and that would be ugly ;)
  564.            */
  565.           
  566.           if (token.type == tEOF)
  567.         {
  568.           done = 1;
  569.           goto end_of_page;
  570.         }
  571.  
  572.           /*
  573.            * Now we know that we are going to make marks to this page
  574.            * => print page header.
  575.            */
  576.  
  577.           if (page_clear)
  578.         {
  579.           PageRange *pr;
  580.  
  581.           current_pagenum++;
  582.           total_pages_in_file++;
  583.  
  584.           /* Check page ranges. */
  585.           if (page_ranges == NULL)
  586.             do_print = 1;
  587.           else
  588.             {
  589.               do_print = 0;
  590.               for (pr = page_ranges; pr; pr = pr->next)
  591.             {
  592.               if (pr->odd || pr->even)
  593.                 {
  594.                   if ((pr->odd && (current_pagenum % 2) == 1)
  595.                   || (pr->even && (current_pagenum % 2) == 0))
  596.                 {
  597.                   do_print = 1;
  598.                   break;
  599.                 }
  600.                 }
  601.               else
  602.                 {
  603.                   if (pr->start <= current_pagenum
  604.                   && current_pagenum <= pr->end)
  605.                 {
  606.                   do_print = 1;
  607.                   break;
  608.                 }
  609.                 }
  610.             }
  611.             }
  612.  
  613.           if (do_print)
  614.             total_pages++;
  615.  
  616.           dump_ps_page_header (fname);
  617.           page_clear = 0;
  618.         }
  619.  
  620.           /* Print line numbers if needed. */
  621.           if (line_numbers && line_column == 0 && token.type != tFORMFEED)
  622.         print_line_number (lx, y, linenumber_space, linenumber_margin,
  623.                    current_file_linenum);
  624.  
  625.           /* Print line highlight. */
  626.           if (line_column == 0 && line_highlight_gray < 1.0)
  627.         output ("%g %g %g %g %g line_highlight\n",
  628.             lx, (y - baselineskip
  629.                  + (font_bbox_lly * Fpt / UNITS_PER_POINT)),
  630.             linewidth, Fpt + baselineskip, line_highlight_gray);
  631.  
  632.           /* Check rest of tokens. */
  633.           switch (token.type)
  634.         {
  635.         case tFORMFEED:
  636.           switch (formfeed_type)
  637.             {
  638.             case FORMFEED_COLUMN:
  639.               goto end_of_column;
  640.               break;
  641.  
  642.             case FORMFEED_PAGE:
  643.               goto end_of_page;
  644.               break;
  645.             }
  646.           break;
  647.  
  648.         case tSTRING:
  649.           output ("%g %g M\n", x, y);
  650.           output ("(%s) s\n", token.u.str);
  651.           x = token.new_x;
  652.           line_column = token.new_col;
  653.           break;
  654.  
  655.         case tCARRIAGE_RETURN:
  656.           /* Just reset the x-coordinate. */
  657.           x = col * d_output_w / (float) num_columns
  658.             + d_output_x_margin + line_indent;
  659.           line_column = 0;
  660.           break;
  661.           
  662.         case tNEWLINE:
  663.           current_file_linenum++;
  664.           /* FALLTHROUGH */
  665.  
  666.         case tWRAPPED_NEWLINE:
  667.           current_linenum++;
  668.           y -= LINESKIP;
  669.           if (current_linenum >= lines_per_page
  670.               || y < d_footer_h + d_output_y_margin)
  671.             goto end_of_column;
  672.  
  673.           x = col * d_output_w / (float) num_columns
  674.             + d_output_x_margin + line_indent;
  675.           line_column = 0;
  676.           break;
  677.  
  678.         case tEPSF:
  679.           /* Count current point movement. */
  680.  
  681.           if (token.flags & F_EPSF_ABSOLUTE_Y)
  682.             token.new_y = ly;
  683.           else
  684.             token.new_y = y;
  685.           token.new_y += token.u.epsf.y - token.u.epsf.h;
  686.  
  687.           if (token.flags & F_EPSF_ABSOLUTE_X)
  688.             token.new_x = lx;
  689.           else
  690.             token.new_x = x;
  691.           token.new_x += token.u.epsf.x;
  692.  
  693.           /* Check flags. */
  694.           
  695.           /* Justification flags overwrite <x_ofs>. */
  696.           if (token.flags & F_EPSF_CENTER)
  697.             token.new_x = lx + (linewidth - token.u.epsf.w) / 2;
  698.           if (token.flags & F_EPSF_RIGHT)
  699.             token.new_x = lx + (linewidth - token.u.epsf.w);
  700.  
  701.           /* Check if eps file does not fit to this column. */
  702.           if ((token.flags & F_EPSF_NO_CPOINT_UPDATE_Y) == 0
  703.               && token.new_y < d_footer_h + d_output_y_margin)
  704.             {
  705.               if (current_linenum == 0)
  706.             {
  707.               /*
  708.                * At the beginning of the column, warn user
  709.                * and print image.
  710.                */
  711.               message (0, _("EPS file \"%s\" is too \
  712. large for page\n"),
  713.                    token.u.epsf.filename);
  714.             }
  715.               else
  716.             {
  717.               /* Must start a new column. */
  718.               reuse_last_token = 1;
  719.               goto end_of_column;
  720.             }
  721.             }
  722.  
  723.           /* Do paste. */
  724.           paste_epsf (&token);
  725.  
  726.           /* Update current point? */
  727.           if (!(token.flags & F_EPSF_NO_CPOINT_UPDATE_Y))
  728.             y = token.new_y;
  729.           if (!(token.flags & F_EPSF_NO_CPOINT_UPDATE_X))
  730.             x = token.new_x + token.u.epsf.w;
  731.           
  732.           if (y < d_footer_h + d_output_y_margin)
  733.             goto end_of_column;
  734.           break;
  735.  
  736.         case tFONT:
  737.           /* Select a new current font. */
  738.           message (1, "^@font=");
  739.           if (token.u.font.name[0] == '\0')
  740.             {
  741.               /* Select the default font. */
  742.               Fpt = default_Fpt;
  743.               Fname = default_Fname;
  744.               output ("/F-gs-font findfont %g scalefont setfont\n",
  745.                   Fpt);
  746.               user_font = 0;
  747.             }
  748.           else
  749.             {
  750.               strhash_put (res_fonts, token.u.font.name,
  751.                    strlen (token.u.font.name) + 1,
  752.                    NULL, NULL);
  753.               output ("/%s /F-gs-user-font MF\n",
  754.                   token.u.font.name);
  755.               output ("/F-gs-user-font findfont %g scalefont \
  756. setfont\n",
  757.                    token.u.font.size);
  758.  
  759.               strcpy (user_font_name, token.u.font.name);
  760.               user_font_pt = token.u.font.size;
  761.               user_font = 1;
  762.               
  763.               Fpt = user_font_pt;
  764.               Fname = user_font_name;
  765.             }
  766.           message (1, "%s %gpt\n", Fname, Fpt);
  767.           read_font_info ();
  768.           break;
  769.  
  770.         case tSETFILENAME:
  771.           strcpy (fname, token.u.filename);
  772.           break;
  773.  
  774.         case tSETPAGENUMBER:
  775.           current_pagenum = token.u.i - 1;
  776.           break;
  777.  
  778.         case tNEWPAGE:
  779.           if (current_linenum >= token.u.i)
  780.             goto end_of_page;
  781.           break;
  782.  
  783.         case tPS:
  784.           output ("%g %g M\n", x, y);
  785.           output ("%s\n", token.u.str);
  786.           xfree (token.u.str);
  787.           break;
  788.  
  789.         case tNONE:
  790.         default:
  791.           fatal("process_file(): got illegal token %d", token.type);
  792.           break;
  793.         }
  794.         }
  795.     end_of_column:
  796.       ;            /* ULTRIX's cc needs this line. */
  797.     }
  798.  
  799.     end_of_page:
  800.       if (!page_clear)
  801.     dump_ps_page_trailer ();
  802.     }
  803.  
  804.   /*
  805.    * Reset print flag to true so all the required document trailers
  806.    * etc. get printed properly.
  807.    */
  808.   do_print = 1;
  809.   
  810.   undivert ();
  811. }
  812.  
  813.  
  814. /*
  815.  * Static functions.
  816.  */
  817.  
  818. /* Help macros. */
  819.  
  820. /* Check if character <ch> fits to current line. */
  821. #define FITS_ON_LINE(ch) ((linepos + CHAR_WIDTH (ch) < linew) || col == 0)
  822.  
  823. /* Is line buffer empty? */
  824. #define BUFFER_EMPTY() (bufpos == 0)
  825.  
  826. /* Unconditionally append character <ch> to the line buffer. */
  827. #define APPEND_CHAR(ch)                 \
  828.   do {                            \
  829.     if (bufpos >= buflen)                \
  830.       {                            \
  831.     buflen += 4096;                    \
  832.     buffer = xrealloc (buffer, buflen);        \
  833.       }                            \
  834.     buffer[bufpos++] = ch;                \
  835.   } while (0)
  836.  
  837. /* 
  838.  * Copy character <ch> (it fits to this line) to output buffer and
  839.  * update current point counters.
  840.  */
  841. #define EMIT(ch)         \
  842.   do {                \
  843.     APPEND_CHAR (ch);        \
  844.     linepos += CHAR_WIDTH (ch);    \
  845.     col++;            \
  846.   } while (0)
  847.  
  848.  
  849. /* Read one special escape from input <fp>. */
  850.  
  851. static struct
  852. {
  853.   char *name;
  854.   SpecialEscape escape;
  855. } escapes[] =
  856.   {
  857.     {"comment",        ESC_COMMENT},
  858.     {"epsf",         ESC_EPSF},
  859.     {"font",         ESC_FONT},
  860.     {"newpage",        ESC_NEWPAGE},
  861.     {"ps",        ESC_PS},
  862.     {"setfilename",    ESC_SETFILENAME},
  863.     {"setpagenumber",    ESC_SETPAGENUMBER},
  864.     {"shade",        ESC_SHADE},
  865.     {NULL, 0},
  866.   };
  867.   
  868.  
  869. static void
  870. read_special_escape (InputStream *is, Token *token)
  871. {
  872.   char escname[256];
  873.   char buf[4096];
  874.   int i, e;
  875.   int ch;
  876.  
  877.   /* Get escape name. */
  878.   for (i = 0; i < sizeof (escname) - 1 && (ch = is_getc (is)) != EOF; i++)
  879.     {
  880.       if (!isalnum (ch))
  881.     {
  882.       is_ungetc (ch, is);
  883.       break;
  884.     }
  885.       else
  886.     escname[i] = ch;
  887.     }
  888.   escname[i] = '\0';
  889.  
  890.   /* Lookup escape. */
  891.   for (e = 0; escapes[e].name; e++)
  892.     if (strcmp (escname, escapes[e].name) == 0)
  893.       break;
  894.   if (escapes[e].name == NULL)
  895.     fatal (_("unknown special escape: %s"), escname);
  896.  
  897.   /*
  898.    * The epsf escape takes optional arguments so it must be handled
  899.    * differently.
  900.    */
  901.   if (escapes[e].escape == ESC_EPSF)
  902.     {
  903.       int i;
  904.       int pw, ph;
  905.       double scale;
  906.  
  907.       token->flags = 0;
  908.       token->u.epsf.x = 0.0;
  909.       token->u.epsf.y = 0.0;
  910.       token->u.epsf.h = 0.0;
  911.       token->u.epsf.pipe = 0;
  912.  
  913.       ch = is_getc (is);
  914.       if (ch == '[')
  915.     {
  916.       /* Read options. */
  917.       while ((ch = is_getc (is)) != EOF && ch != ']')
  918.         {
  919.           switch (ch)
  920.         {
  921.         case 'c':    /* center justification */
  922.           token->flags &= ~M_EPSF_JUSTIFICATION;
  923.           token->flags |= F_EPSF_CENTER;
  924.           break;
  925.  
  926.         case 'n':    /* no current point update */
  927.           /* Check the next character. */
  928.           ch = is_getc (is);
  929.           switch (ch)
  930.             {
  931.             case 'x':
  932.               token->flags |= F_EPSF_NO_CPOINT_UPDATE_X;
  933.               break;
  934.  
  935.             case 'y':
  936.               token->flags |= F_EPSF_NO_CPOINT_UPDATE_Y;
  937.               break;
  938.  
  939.             default:
  940.               is_ungetc (ch, is);
  941.               token->flags |= F_EPSF_NO_CPOINT_UPDATE_X;
  942.               token->flags |= F_EPSF_NO_CPOINT_UPDATE_Y;
  943.               break;
  944.             }
  945.           break;
  946.  
  947.         case 'r':    /* right justification */
  948.           token->flags &= ~M_EPSF_JUSTIFICATION;
  949.           token->flags |= F_EPSF_RIGHT;
  950.           break;
  951.  
  952.  
  953.         case 's':    /* scale */
  954.           /* Check the next character. */
  955.           ch = is_getc (is);
  956.           switch (ch)
  957.             {
  958.             case 'x':
  959.               token->flags |= F_EPSF_SCALE_X;
  960.               token->u.epsf.xscale = read_float (is, 0, 1);
  961.               break;
  962.  
  963.             case 'y':
  964.               token->flags |= F_EPSF_SCALE_Y;
  965.               token->u.epsf.yscale = read_float (is, 0, 0);
  966.               break;
  967.  
  968.             default:
  969.               is_ungetc (ch, is);
  970.               token->flags |= F_EPSF_SCALE_X;
  971.               token->flags |= F_EPSF_SCALE_Y;
  972.               token->u.epsf.xscale = token->u.epsf.yscale
  973.             = read_float (is, 0, 1);
  974.               break;
  975.             }
  976.           break;
  977.  
  978.         case 'x':    /* x-position */
  979.           token->u.epsf.x = read_float (is, 1, 1);
  980.  
  981.           /* Check the next character. */
  982.           ch = is_getc (is);
  983.           switch (ch)
  984.             {
  985.             case 'a':
  986.               token->flags |= F_EPSF_ABSOLUTE_X;
  987.               break;
  988.  
  989.             default:
  990.               is_ungetc (ch, is);
  991.               break;
  992.             }
  993.           break;
  994.  
  995.         case 'y':    /* y-position */
  996.           token->u.epsf.y = - read_float (is, 1, 0);
  997.  
  998.           /* Check the next character. */
  999.           ch = is_getc (is);
  1000.           switch (ch)
  1001.             {
  1002.             case 'a':
  1003.               token->flags |= F_EPSF_ABSOLUTE_Y;
  1004.               break;
  1005.  
  1006.             default:
  1007.               is_ungetc (ch, is);
  1008.               break;
  1009.             }
  1010.           break;
  1011.  
  1012.         case 'h':    /* height */
  1013.           token->u.epsf.h = read_float (is, 1, 0);
  1014.           break;
  1015.  
  1016.         case ' ':
  1017.         case '\t':
  1018.           break;
  1019.  
  1020.         default:
  1021.           fatal (_("illegal option %c for ^@epsf escape"), ch);
  1022.         }
  1023.         }          
  1024.       if (ch != ']')
  1025.         fatal (_("malformed ^@epsf escape: no ']' after options"));
  1026.  
  1027.       ch = is_getc (is);
  1028.     }
  1029.       if (ch == '{')
  1030.     {
  1031.       /* Read filename. */
  1032.       for (i = 0; (ch = is_getc (is)) != EOF && ch != '}'; i++)
  1033.         {
  1034.           token->u.epsf.filename[i] = ch;
  1035.           if (i + 1 >= sizeof (token->u.epsf.filename))
  1036.         fatal (_("too long file name for ^@epsf escape:\n%.*s"),
  1037.                i, token->u.epsf.filename);
  1038.         }
  1039.       if (ch == EOF)
  1040.         fatal (_("unexpected EOF while scanning ^@epsf escape"));
  1041.  
  1042.       token->u.epsf.filename[i] = '\0';
  1043.       token->type = tEPSF;
  1044.     }
  1045.       else
  1046.     fatal (_("malformed ^@epsf escape: no '{' found"));
  1047.  
  1048.       /* 
  1049.        * Now we have a valid epsf-token in <token>.  Let's read BoundingBox
  1050.        * and do some calculations.
  1051.        */
  1052.       if (!recognize_eps_file (token))
  1053.     /* Recognize eps has already printed error message so we are done. */
  1054.     token->type = tNONE;
  1055.       else
  1056.     {
  1057.       /* Some fixups for x and y dimensions. */
  1058.       token->u.epsf.y += LINESKIP - 1;
  1059.       if (token->u.epsf.h != 0.0)
  1060.         token->u.epsf.h -= 1.0;
  1061.  
  1062.       /* Count picture's width and height. */
  1063.  
  1064.       pw = token->u.epsf.urx - token->u.epsf.llx;
  1065.       ph = token->u.epsf.ury - token->u.epsf.lly;
  1066.  
  1067.       /* The default scale. */
  1068.       if (token->u.epsf.h == 0.0)
  1069.         scale = 1.0;
  1070.       else
  1071.         scale = token->u.epsf.h / ph;
  1072.  
  1073.       if ((token->flags & F_EPSF_SCALE_X) == 0)
  1074.         token->u.epsf.xscale = scale;
  1075.       if ((token->flags & F_EPSF_SCALE_Y) == 0)
  1076.         token->u.epsf.yscale = scale;
  1077.  
  1078.       pw *= token->u.epsf.xscale;
  1079.       ph *= token->u.epsf.yscale;
  1080.  
  1081.       token->u.epsf.w = pw;
  1082.       token->u.epsf.h = ph;
  1083.     }
  1084.     }
  1085.   else if (escapes[e].escape == ESC_COMMENT)
  1086.     {
  1087.       /* Comment the rest of this line. */
  1088.       while ((ch = is_getc (is)) != EOF && ch != nl)
  1089.     ;
  1090.       token->type = tNONE;
  1091.     }
  1092.   else 
  1093.     {
  1094.       char *cp;
  1095.       int parenlevel;
  1096.  
  1097.       /*
  1098.        * Handle the rest of the escapes.
  1099.        */
  1100.  
  1101.       /* Read argument. */
  1102.       ch = is_getc (is);
  1103.       if (ch != '{')
  1104.     fatal (_("malformed %s escape: no '{' found"), escapes[e].name);
  1105.  
  1106.       parenlevel = 0;
  1107.       for (i = 0;
  1108.        (ch = is_getc (is)) != EOF && (parenlevel > 0 || ch != '}'); i++)
  1109.     {
  1110.       if (ch == '{')
  1111.         parenlevel++;
  1112.       else if (ch == '}')
  1113.         parenlevel--;
  1114.  
  1115.       buf[i] = ch;
  1116.       if (i + 1 >= sizeof (buf))
  1117.         fatal (_("too long argument for %s escape:\n%.*s"),
  1118.            escapes[i].name, i, buf);
  1119.     }
  1120.       buf[i] = '\0';
  1121.  
  1122.       /* And now handle the escape. */
  1123.       switch (escapes[e].escape)
  1124.     {
  1125.     case ESC_FONT:
  1126.       strcpy (token->u.font.name, buf);
  1127.  
  1128.       /* Check for the default font. */
  1129.       if (strcmp (token->u.font.name, "default") == 0)
  1130.         token->u.font.name[0] = '\0';
  1131.       else
  1132.         {
  1133.           if (!parse_font_spec (token->u.font.name, &cp,
  1134.                     &token->u.font.size))
  1135.         fatal (_("malformed font spec for ^@font escape: %s"),
  1136.                token->u.font.name);
  1137.           
  1138.           strcpy (token->u.font.name, cp);
  1139.           xfree (cp);
  1140.         }
  1141.       token->type = tFONT;
  1142.       break;
  1143.  
  1144.     case ESC_SHADE:
  1145.       line_highlight_gray = atof (buf);
  1146.       if (line_highlight_gray < 0.0 || line_highlight_gray > 1.0)
  1147.         fatal (_("invalid value for ^@shade escape: %s"), buf);
  1148.  
  1149.       token->type = tNONE;
  1150.       break;
  1151.  
  1152.     case ESC_SETFILENAME:
  1153.       strcpy (token->u.filename, buf);
  1154.       token->type = tSETFILENAME;
  1155.       break;
  1156.  
  1157.     case ESC_SETPAGENUMBER:
  1158.       token->u.i = atoi (buf);
  1159.       token->type = tSETPAGENUMBER;
  1160.       break;
  1161.  
  1162.     case ESC_NEWPAGE:
  1163.       if (i == 0)
  1164.         token->u.i = 1;    /* The default is the first line. */
  1165.       else
  1166.         token->u.i = atoi (buf);
  1167.       token->type = tNEWPAGE;
  1168.       break;
  1169.  
  1170.     case ESC_PS:
  1171.       token->u.str = xstrdup (buf);
  1172.       token->type = tPS;
  1173.       break;
  1174.  
  1175.     default:
  1176.       /* NOTREACHED */
  1177.       abort ();
  1178.       break;
  1179.     }
  1180.     }
  1181. }
  1182.  
  1183.  
  1184. /* Get next token from input file <fp>. */
  1185. static void
  1186. get_next_token (InputStream *is, double linepos, unsigned int col,
  1187.         double linew, Token *token)
  1188. {
  1189.   static unsigned char *buffer = NULL; /* output buffer */
  1190.   static unsigned int buflen = 0; /* output buffer's length */
  1191.   unsigned int bufpos = 0;    /* current position in output buffer */
  1192.   int ch = 0;
  1193.   int done = 0;
  1194.   int i;
  1195.   static int pending_token = tNONE;
  1196.  
  1197.   if (pending_token != tNONE)
  1198.     {
  1199.       token->type = pending_token;
  1200.       pending_token = tNONE;
  1201.       return;
  1202.     }
  1203.  
  1204. #define DONE_DONE 1
  1205. #define DONE_WRAP 2
  1206.  
  1207.   while (!done)
  1208.     {
  1209.       ch = is_getc (is);
  1210.       switch (ch)
  1211.     {
  1212.     case EOF:
  1213.       if (BUFFER_EMPTY ())
  1214.         {
  1215.           token->type = tEOF;
  1216.           return;
  1217.         }
  1218.  
  1219.       done = DONE_DONE;
  1220.       break;
  1221.  
  1222.     case '(':
  1223.     case ')':
  1224.     case '\\':
  1225.       if (FITS_ON_LINE (ch))
  1226.         {
  1227.           APPEND_CHAR ('\\');
  1228.           EMIT (ch);
  1229.         }
  1230.       else
  1231.         {
  1232.           is_ungetc (ch, is);
  1233.           done = DONE_WRAP;
  1234.         }
  1235.       break;
  1236.  
  1237.     case '\r':
  1238.     case '\n':
  1239.       /*
  1240.        * One of these is the newline character and the other one
  1241.        * is carriage return.
  1242.        */
  1243.       if (ch == nl)
  1244.         {
  1245.           /* The newline character. */
  1246.           if (BUFFER_EMPTY ())
  1247.         {
  1248.           token->type = tNEWLINE;
  1249.           return;
  1250.         }
  1251.           else
  1252.         {
  1253.           is_ungetc (ch, is);
  1254.           done = DONE_DONE;
  1255.         }
  1256.         }
  1257.       else
  1258.         {
  1259.           /* The carriage return character. */
  1260.           if (BUFFER_EMPTY ())
  1261.         {
  1262.           token->type = tCARRIAGE_RETURN;
  1263.           return;
  1264.         }
  1265.           else
  1266.         {
  1267.           is_ungetc (ch, is);
  1268.           done = DONE_DONE;
  1269.         }
  1270.         }
  1271.       break;
  1272.  
  1273.     case '\t':
  1274.       if (font_is_fixed)
  1275.         {
  1276.           i = tabsize - (col % tabsize);
  1277.           for (; i > 0; i--)
  1278.         {
  1279.           if (FITS_ON_LINE (' '))
  1280.             EMIT (' ');
  1281.           else
  1282.             {
  1283.               done = DONE_WRAP;
  1284.               break;
  1285.             }
  1286.         }
  1287.         }
  1288.       else
  1289.         {
  1290.           /* Proportional font. */
  1291.  
  1292.           double grid = tabsize * CHAR_WIDTH ('m');
  1293.           col++;
  1294.  
  1295.           /* Move linepos to the next multiple of <grid>. */
  1296.           linepos = ((int) (linepos / grid) + 1) * grid;
  1297.           if (linepos >= linew)
  1298.         done = DONE_WRAP;
  1299.           else
  1300.         done = DONE_DONE;
  1301.         }
  1302.       break;
  1303.  
  1304.     case '\f':
  1305.       if (BUFFER_EMPTY ())
  1306.         {
  1307.           if (interpret_formfeed)
  1308.         token->type = tFORMFEED;
  1309.           else
  1310.         token->type = tNEWLINE;
  1311.           return;
  1312.         }
  1313.       else
  1314.         {
  1315.           is_ungetc (ch, is);
  1316.           done = DONE_DONE;
  1317.         }
  1318.       break;
  1319.  
  1320.     default:
  1321.       /* Handle special escapes. */
  1322.       if (special_escapes && ch == escape_char)
  1323.         {
  1324.           if (BUFFER_EMPTY ())
  1325.         {
  1326.           /* Interpret special escapes. */
  1327.           read_special_escape (is, token);
  1328.           if (token->type != tNONE)
  1329.             return;
  1330.  
  1331.           /*
  1332.            * Got tNONE special escape => read_special_escape()
  1333.            * has already done what was needed.  Just read more.
  1334.            */
  1335.           break;
  1336.         }
  1337.           else
  1338.         {
  1339.           is_ungetc (ch, is);
  1340.           done = DONE_DONE;
  1341.           break;
  1342.         }
  1343.         }
  1344.  
  1345.       /* Handle backspace character. */
  1346.       if (ch == bs)
  1347.         {
  1348.           if (BUFFER_EMPTY () || !EXISTS (buffer[bufpos - 1]))
  1349.         linepos -= CHAR_WIDTH ('m');
  1350.           else
  1351.         linepos -= CHAR_WIDTH (buffer[bufpos - 1]);
  1352.  
  1353.           done = DONE_DONE;
  1354.           break;
  1355.         }
  1356.  
  1357.       /* Check normal characters. */
  1358.       if (EXISTS (ch))
  1359.         {
  1360.           if (FITS_ON_LINE (ch))
  1361.         {
  1362.           /*
  1363.            * Print control characters (and optionally
  1364.            * characters greater than 127) in the escaped form
  1365.            * so PostScript interpreter will not hang on them.
  1366.            */
  1367.           if (ch < 040 || (clean_7bit && ch >= 0200))
  1368.             {
  1369.               char buf[10];
  1370.  
  1371.               sprintf (buf, "\\%03o", ch);
  1372.               for (i = 0; buf[i]; i++)
  1373.             APPEND_CHAR (buf[i]);
  1374.               
  1375.               /* Update current point counters manually. */
  1376.               linepos += CHAR_WIDTH (ch);
  1377.               col++;
  1378.             }
  1379.           else
  1380.             EMIT (ch);
  1381.         }
  1382.           else
  1383.         {
  1384.           is_ungetc (ch, is);
  1385.           done = DONE_WRAP;
  1386.         }
  1387.         }
  1388.       else if (ISPRINT (ch))
  1389.         {
  1390.           /* Printable, but do not exists in this font. */
  1391.           if (FITS_ON_LINE ('?'))
  1392.         {
  1393.           EMIT ('?');
  1394.           if (missing_chars[ch]++ == 0)
  1395.             num_missing_chars++;
  1396.         }
  1397.           else
  1398.         {
  1399.           is_ungetc (ch, is);
  1400.           done = DONE_WRAP;
  1401.         }
  1402.         }
  1403.       else
  1404.         {
  1405.           char buf[20];
  1406.           double len = 0.0;
  1407.  
  1408.           /*
  1409.            * Non-printable and does not exist in current font, print
  1410.            * it in the format specified by non_printable_format.
  1411.            */
  1412.           
  1413.           if (non_printable_chars[ch]++ == 0)
  1414.         num_non_printable_chars++;
  1415.  
  1416.           switch (non_printable_format)
  1417.         {
  1418.         case NPF_SPACE:
  1419.           strcpy (buf, " ");
  1420.           break;
  1421.  
  1422.         case NPF_QUESTIONMARK:
  1423.           strcpy (buf, "?");
  1424.           break;
  1425.  
  1426.         case NPF_CARET:
  1427.           if (ch < 0x20)
  1428.             {
  1429.               buf[0] = '^';
  1430.               buf[1] = '@' + ch;
  1431.               buf[2] = '\0';
  1432.               break;
  1433.             }
  1434.           /* FALLTHROUGH */
  1435.  
  1436.         case NPF_OCTAL:
  1437.           sprintf (buf, "\\%03o", ch);
  1438.           break;
  1439.         }
  1440.  
  1441.           /* Count length. */
  1442.           for (i = 0; buf[i]; i++)
  1443.         len += CHAR_WIDTH (buf[i]);
  1444.  
  1445.           if (linepos + len < linew || col == 0)
  1446.         {
  1447.           /* Print it. */
  1448.           for (i = 0; buf[i]; i++)
  1449.             {
  1450.               if (buf[i] == '\\')
  1451.             APPEND_CHAR ('\\'); /* Escape '\\' characters. */
  1452.               EMIT (buf[i]);
  1453.             }
  1454.         }
  1455.           else
  1456.         {
  1457.           is_ungetc (ch, is);
  1458.           done = DONE_WRAP;
  1459.         }
  1460.         }
  1461.       break;
  1462.     }
  1463.     }
  1464.  
  1465.   /* Got a string. */
  1466.  
  1467.   /* Check for wrapped line. */
  1468.   if (done == DONE_WRAP)
  1469.     {
  1470.       /* This line is too long. */
  1471.       num_truncated_lines++;
  1472.  
  1473.       ch = nl;
  1474.       if (truncate_lines)
  1475.     {
  1476.       /* Truncate this line. */
  1477.       while ((ch = is_getc (is)) != EOF && ch != nl)
  1478.         ;
  1479.     }
  1480.       if (ch == nl)
  1481.     {
  1482.       if (truncate_lines)
  1483.         pending_token = tNEWLINE;
  1484.       else
  1485.         pending_token = tWRAPPED_NEWLINE;
  1486.     }
  1487.       else
  1488.     pending_token = tEOF;
  1489.     }
  1490.  
  1491.   APPEND_CHAR ('\0');
  1492.   token->type = tSTRING;
  1493.   token->u.str = (char *) buffer;
  1494.   token->new_x = linepos;
  1495.   token->new_col = col;
  1496. }
  1497.  
  1498.  
  1499. static void
  1500. dump_ps_page_header (char *fname)
  1501. {
  1502.   char buf[512];
  1503.   char *ftail;
  1504.   int got, i;
  1505.   char *cp, *cp2;
  1506.  
  1507.   /* Create fdir and ftail. */
  1508.   ftail = strrchr (fname, '/');
  1509.   if (ftail == NULL)
  1510.     {
  1511.       buf[0] = '\0';
  1512.       ftail = fname;
  1513.     }
  1514.   else
  1515.     {
  1516.       ftail++;
  1517.       strncpy (buf, fname, ftail - fname);
  1518.       buf[ftail - fname] = '\0';
  1519.     }
  1520.  
  1521.   /* Page start comment. */
  1522.   switch (page_label)
  1523.     {
  1524.     case LABEL_SHORT:
  1525.       output ("%%%%Page: (%d) %d\n", current_pagenum, total_pages);
  1526.       break;
  1527.  
  1528.     case LABEL_LONG:
  1529.       output ("%%%%Page: (%s:%3d) %d\n", ftail, current_pagenum, total_pages);
  1530.       break;
  1531.     }
  1532.  
  1533.   /* 
  1534.    * Page Setup.
  1535.    */
  1536.  
  1537.   output ("%%%%BeginPageSetup\n");
  1538.  
  1539.   output ("_S\n");
  1540.  
  1541.   if (landscape)
  1542.     {
  1543.       output ("90 rotate\n");
  1544.       output ("%d %d translate\n", media->lly, media->llx - media->w);
  1545.     }
  1546.   else
  1547.     output ("%d %d translate\n", media->llx, media->lly);
  1548.  
  1549.   output ("/pagenum %d def\n", current_pagenum);
  1550.   output ("/fname (%s) def\n", fname);
  1551.   output ("/fdir (%s) def\n", buf);
  1552.   output ("/ftail (%s) def\n", ftail);
  1553.  
  1554.   /* Do we have a pending ^@font{} font? */
  1555.   if (user_font)
  1556.     {
  1557.       output ("/%s /F-gs-user-font MF\n", Fname);
  1558.       output ("/F-gs-user-font findfont %g scalefont setfont\n", Fpt);
  1559.     }
  1560.  
  1561.   /* Dump user defined strings. */
  1562.   if (count_key_value_set (user_strings) > 0)
  1563.     {
  1564.       output ("%% User defined strings:\n");
  1565.       for (got = strhash_get_first (user_strings, &cp, &i, (void **) &cp2);
  1566.        got;
  1567.        got = strhash_get_next (user_strings, &cp, &i, (void **) &cp2))
  1568.     {
  1569.       cp2 = format_user_string (cp2);
  1570.       output ("/%s (%s) def\n", cp, cp2);
  1571.       xfree (cp2);
  1572.     }
  1573.     }
  1574.  
  1575.   output ("%%%%EndPageSetup\n");
  1576.  
  1577.  
  1578.   /* 
  1579.    * Mark standard page decorations.
  1580.    */
  1581.  
  1582.   /* Highlight bars. */
  1583.   if (highlight_bars)
  1584.     output ("%d %f %d %f highlight_bars\n", highlight_bars,
  1585.         LINESKIP, d_output_y_margin, highlight_bar_gray);
  1586.   
  1587.   /* Underlay. */
  1588.   if (underlay != NULL)
  1589.     {
  1590.       if (ul_position_p || ul_angle_p)
  1591.     output ("user_underlay\n");
  1592.       else
  1593.     output ("underlay\n");
  1594.     }
  1595.  
  1596.   /* Column lines. */
  1597.   if (num_columns > 1 && (header == HDR_FANCY || borders))
  1598.     output ("column_lines\n");
  1599.  
  1600.   /* Borders around columns. */
  1601.   if (borders)
  1602.     output ("column_borders\n");
  1603.  
  1604.   /* Header. */
  1605.   switch (header)
  1606.     {
  1607.     case HDR_NONE:
  1608.       break;
  1609.  
  1610.     case HDR_SIMPLE:
  1611.     case HDR_FANCY:
  1612.       output ("do_header\n");
  1613.       break;
  1614.     }
  1615. }
  1616.  
  1617.  
  1618. static void
  1619. dump_ps_page_trailer ()
  1620. {
  1621.   output ("_R\n");
  1622.   output ("S\n");
  1623. }
  1624.  
  1625.  
  1626. static int
  1627. recognize_eps_file (Token *token)
  1628. {
  1629.   int i;
  1630.   char filename[512];
  1631.   char buf[4096];
  1632.   int line;
  1633.   int valid_epsf;
  1634.   float llx, lly, urx, ury;
  1635.  
  1636.   message (1, "^@epsf=\"%s\"\n", token->u.epsf.filename);
  1637.  
  1638.   i = strlen (token->u.epsf.filename);
  1639.   if (i > 0 && token->u.epsf.filename[i - 1] == '|')
  1640.     {
  1641.       /* Read EPS data from pipe. */
  1642.       token->u.epsf.pipe = 1;
  1643.       token->u.epsf.filename[i - 1] = '\0';
  1644.       token->u.epsf.fp = popen (token->u.epsf.filename, "r");
  1645.       if (token->u.epsf.fp == NULL)
  1646.     {
  1647.       message (0, _("epsf: couldn't open pipe to command \"%s\": %s\n"),
  1648.            token->u.epsf.filename, strerror (errno));
  1649.       return 0;
  1650.     }
  1651.     }
  1652.   else
  1653.     {
  1654.       /* Read EPS data from file. */
  1655.       tilde_subst (token->u.epsf.filename, filename);
  1656.  
  1657.       token->u.epsf.fp = fopen (filename, "rb");
  1658.       if (token->u.epsf.fp == NULL)
  1659.     {
  1660.       if (token->u.epsf.filename[0] != '/')
  1661.         {
  1662.           /* Name is not absolute, let's lookup path. */
  1663.           FileLookupCtx ctx;
  1664.  
  1665.           strcpy (ctx.name, token->u.epsf.filename);
  1666.           strcpy (ctx.suffix, "");
  1667.  
  1668.           if (pathwalk (libpath, file_lookup, &ctx))
  1669.         token->u.epsf.fp = fopen (ctx.fullname, "rb");
  1670.         }
  1671.       if (token->u.epsf.fp == NULL)
  1672.         {
  1673.           message (0, _("couldn't open EPS file \"%s\": %s\n"),
  1674.                token->u.epsf.filename, strerror (errno));
  1675.           return 0;
  1676.         }
  1677.     }
  1678.     }
  1679.  
  1680.   /* Find BoundingBox DSC comment. */
  1681.  
  1682.   line = 0;
  1683.   valid_epsf = 0;
  1684.   token->u.epsf.skipbuf = NULL;
  1685.   token->u.epsf.skipbuf_len = 0;
  1686.   token->u.epsf.skipbuf_pos = 0;
  1687.  
  1688.   while (fgets (buf, sizeof (buf), token->u.epsf.fp))
  1689.     {
  1690.       line++;
  1691.  
  1692.       /* Append data to the skip buffer. */
  1693.       i = strlen (buf);
  1694.       if (i + token->u.epsf.skipbuf_pos >= token->u.epsf.skipbuf_len)
  1695.     {
  1696.       token->u.epsf.skipbuf_len += 8192;
  1697.       token->u.epsf.skipbuf = xrealloc (token->u.epsf.skipbuf,
  1698.                         token->u.epsf.skipbuf_len);
  1699.     }
  1700.       memcpy (token->u.epsf.skipbuf + token->u.epsf.skipbuf_pos, buf, i);
  1701.       token->u.epsf.skipbuf_pos += i;
  1702.  
  1703.       /* Check the "%!" magic cookie. */
  1704.       if (line == 1)
  1705.     {
  1706.       if (buf[0] != '%' || buf[1] != '!')
  1707.         {
  1708.           message (0,
  1709.                _("EPS file \"%s\" does not start with \"%%!\" magic\n"),
  1710.                token->u.epsf.filename);
  1711.           break;
  1712.         }
  1713.     }
  1714.  
  1715. #define BB_DSC "%%BoundingBox:"
  1716.  
  1717.       if (strncmp (buf, BB_DSC, strlen (BB_DSC)) == 0)
  1718.     {
  1719.       i = sscanf (buf + strlen (BB_DSC), "%f %f %f %f",
  1720.               &llx, &lly, &urx, &ury);
  1721.       if (i != 4)
  1722.         {
  1723.           /* (atend) ? */
  1724.  
  1725.           /* Skip possible whitespace. */
  1726.           for (i = strlen (BB_DSC);
  1727.            buf[i] && (buf[i] == ' ' || buf[i] == '\t');
  1728.            i++)
  1729.         ;
  1730. #define BB_DSC_ATEND "(atend)"
  1731.           if (strncmp (buf + i, BB_DSC_ATEND, strlen (BB_DSC_ATEND)) != 0)
  1732.         {
  1733.           /* No, this BoundingBox comment is corrupted. */
  1734.           message (0, _("EPS file \"%s\" contains malformed \
  1735. %%%%BoundingBox row:\n\"%.*s\"\n"),
  1736.                token->u.epsf.filename, strlen (buf) - 1, buf);
  1737.           break;
  1738.         }
  1739.         }
  1740.       else
  1741.         {
  1742.           /* It was a valid EPS file. */
  1743.  
  1744.           /* We store bounding box in int format. */
  1745.           token->u.epsf.llx = llx;
  1746.           token->u.epsf.lly = lly;
  1747.           token->u.epsf.urx = urx;
  1748.           token->u.epsf.ury = ury;
  1749.  
  1750.           valid_epsf = 1;
  1751.           break;
  1752.         }
  1753.     }
  1754.     }
  1755.  
  1756.   /* Check that we found the BoundingBox comment. */
  1757.   if (!valid_epsf)
  1758.     {
  1759.       message (0, _("EPS file \"%s\" is not a valid EPS file\n"),
  1760.            token->u.epsf.filename);
  1761.       if (token->u.epsf.pipe)
  1762.     pclose (token->u.epsf.fp);
  1763.       else
  1764.     fclose (token->u.epsf.fp);
  1765.       xfree (token->u.epsf.skipbuf);
  1766.       return 0;
  1767.     }
  1768.  
  1769.   message (2, "BoundingBox: %d %d %d %d\n",
  1770.        token->u.epsf.llx, token->u.epsf.lly,
  1771.        token->u.epsf.urx, token->u.epsf.ury);
  1772.  
  1773.   return 1;
  1774. }
  1775.  
  1776.  
  1777. static void
  1778. paste_epsf (Token *token)
  1779. {
  1780.   char buf[4096];
  1781.   int i;
  1782.   
  1783.   /* EPSF import header. */
  1784.   output ("BeginEPSF\n");
  1785.   output ("%g %g translate\n", token->new_x, token->new_y);
  1786.   output ("%g %g scale\n", token->u.epsf.xscale, token->u.epsf.yscale);
  1787.   output ("%d %d translate\n", -token->u.epsf.llx,
  1788.       -token->u.epsf.lly);
  1789.   output ("%d %d %d %d Box clip newpath\n",
  1790.       token->u.epsf.llx - 1,
  1791.       token->u.epsf.lly - 1,
  1792.       token->u.epsf.urx - token->u.epsf.llx + 2,
  1793.       token->u.epsf.ury - token->u.epsf.lly + 2);
  1794.   output ("%%%%BeginDocument: %s%s\n", token->u.epsf.filename,
  1795.       token->u.epsf.pipe ? "|" : "");
  1796.  
  1797.   if (do_print)
  1798.     {
  1799.       /* Dump skip buffer. */
  1800.       fwrite (token->u.epsf.skipbuf, 1, token->u.epsf.skipbuf_pos, cofp);
  1801.  
  1802.       /* Dump file. */
  1803.       while ((i = fread (buf, 1, sizeof (buf), token->u.epsf.fp)) != 0)
  1804.     fwrite (buf, 1, i, cofp);
  1805.     }
  1806.  
  1807.   /* Add a newline to keep comments correct */
  1808.   output ("\n");
  1809.  
  1810.   /* EPSF import trailer. */
  1811.   output ("%%%%EndDocument\n");
  1812.   output ("EndEPSF\n");
  1813.  
  1814.   /* Cleanup. */
  1815.   if (token->u.epsf.pipe)
  1816.     pclose (token->u.epsf.fp);
  1817.   else
  1818.     fclose (token->u.epsf.fp);
  1819.   xfree (token->u.epsf.skipbuf);
  1820. }
  1821.  
  1822.  
  1823. static double
  1824. read_float (InputStream *is, int units, int horizontal)
  1825. {
  1826.   char buf[256];
  1827.   int i, ch;
  1828.   double val;
  1829.  
  1830.   for (i = 0; (i < sizeof (buf) - 1
  1831.            && (ch = is_getc (is)) != EOF
  1832.            && ISNUMBERDIGIT (ch)); 
  1833.        i++)
  1834.     buf[i] = ch;
  1835.   buf[i] = '\0';
  1836.   if (ch != EOF)
  1837.     is_ungetc (ch, is);
  1838.  
  1839.   val = atof (buf);
  1840.  
  1841.   if (units)
  1842.     {
  1843.       /* Get unit. */
  1844.       ch = is_getc (is);
  1845.       switch (ch)
  1846.     {
  1847.     case 'c':        /* centimeters */
  1848.       val *= 72 / 2.54;
  1849.       break;
  1850.  
  1851.     case 'p':        /* PostScript points */
  1852.       break;
  1853.  
  1854.     case 'i':        /* inches */
  1855.       val *= 72;
  1856.       break;
  1857.  
  1858.     default:
  1859.       is_ungetc (ch, is);
  1860.       /* FALLTHROUGH */
  1861.  
  1862.     case 'l':        /* lines or characters */
  1863.       if (horizontal)
  1864.         val *= CHAR_WIDTH ('m');
  1865.       else
  1866.         val *= LINESKIP;
  1867.       break;
  1868.     }
  1869.     }
  1870.  
  1871.   return val;
  1872. }
  1873.  
  1874.  
  1875. /* Magics used to recognize different pass-through files. */
  1876. static struct
  1877. {
  1878.   char *magic;
  1879.   unsigned int magiclen;
  1880.   char *name;
  1881.   int revert_delta;
  1882. } pass_through_magics[] =
  1883.   {
  1884.     {"%!",     2, "PostScript",     -2},
  1885.     {"\004%!",    3, "PostScript",     -2},
  1886.     {"\033E",    2, "PCL",        -2},
  1887.     {"\033%",    2, "PCL",        -2},
  1888.     {NULL, 0, NULL, 0},
  1889.   };
  1890.  
  1891.  
  1892. static int
  1893. do_pass_through (char *fname, InputStream *is)
  1894. {
  1895.   int ch;
  1896.   unsigned long saved_pos = is->bufpos;
  1897.   int i, j;
  1898.  
  1899.   /*
  1900.    * Try to recognize pass-through files.
  1901.    */
  1902.  
  1903.   for (i = 0; pass_through_magics[i].magic; i++)
  1904.     {
  1905.       for (j = 0; j < pass_through_magics[i].magiclen; j++)
  1906.     {
  1907.       ch = is_getc (is);
  1908.       if (ch == EOF
  1909.           || ch != (unsigned char) pass_through_magics[i].magic[j])
  1910.         break;
  1911.     }
  1912.  
  1913.       if (j >= pass_through_magics[i].magiclen)
  1914.     /* The <i>th one matched. */
  1915.     break;
  1916.       
  1917.       /* Try the next one, but first, seek the input stream to its start. */
  1918.       is->bufpos = saved_pos;
  1919.     }
  1920.  
  1921.   /* Did we find any? */
  1922.   if (pass_through_magics[i].magic == NULL)
  1923.     /* No we didn't. */
  1924.     return 0;
  1925.  
  1926.   /* Yes, it really is a pass-through file.  Now do the pass through. */
  1927.  
  1928.   if (ps_header_dumped)
  1929.     {
  1930.       /* A pass-through file between normal ASCII files, obey DSC. */
  1931.  
  1932.       /* 
  1933.        * XXX I don't know how to handle PCL files... Let's hope none
  1934.        * mixes them with the normal ASCII files.
  1935.        */
  1936.  
  1937.       output ("%%%%Page: (%s) -1\n", fname);
  1938.       output ("_S\n");
  1939.       output ("%%%%BeginDocument: %s\n", fname);
  1940.     }
  1941.  
  1942.   message (1, _("passing through %s file \"%s\"\n"),
  1943.        pass_through_magics[i].name, fname);
  1944.   is->bufpos += pass_through_magics[i].revert_delta;
  1945.   do
  1946.     {
  1947.       /* Note: this will be written directly to the <ofp>. */
  1948.       fwrite (is->buf + is->bufpos, 1, is->data_in_buf - is->bufpos, ofp);
  1949.       is->bufpos = is->data_in_buf;
  1950.  
  1951.       /* Read more data to the input buffer. */
  1952.       ch = is_getc (is);
  1953.       is->bufpos = 0;
  1954.     }
  1955.   while (ch != EOF);
  1956.  
  1957.   if (ps_header_dumped)
  1958.     {
  1959.       /*
  1960.        * XXX How to end a PCL file mixed between ASCII files?
  1961.        */
  1962.       output ("%%%%EndDocument\n");
  1963.       output ("_R\n");
  1964.     }
  1965.  
  1966.   return 1;
  1967. }
  1968.  
  1969.  
  1970. static void
  1971. print_line_number (double x, double y, double space, double margin,
  1972.            unsigned int linenum)
  1973. {
  1974.   double len = 0.0;
  1975.   char buf[20];
  1976.   int i;
  1977.  
  1978.   /* Do not print linenumbers for wrapped lines. */
  1979.   if (linenum == print_line_number_last)
  1980.     return;
  1981.   print_line_number_last = linenum;
  1982.  
  1983.   /* Count linenumber string length. */
  1984.   sprintf (buf, "%d", linenum);
  1985.   for (i = 0; buf[i]; i++)
  1986.     len += CHAR_WIDTH (buf[i]);
  1987.  
  1988.   output ("%g %g M (%s:) s\n", x + space - len, y, buf);
  1989. }
  1990.  
  1991.  
  1992. static void
  1993. #if defined(HAVE_STDARG_H) && defined(__STDC__) && __STDC__
  1994.   output (char *fmt, ...)
  1995. #else
  1996.   output (va_alist)
  1997.      va_dcl
  1998. #endif
  1999. {
  2000.   va_list args;
  2001. #if defined(HAVE_STDARG_H) && defined(__STDC__) && __STDC__
  2002.   va_start (args, fmt);
  2003. #else
  2004.   char *fmt;
  2005.   
  2006.   va_start (args);
  2007.   fmt = va_arg (args, char *);
  2008. #endif
  2009.  
  2010.   if (cofp == NULL)
  2011.     cofp = ofp;
  2012.  
  2013.   if (do_print)
  2014.     {
  2015. #if HAVE_VPRINTF
  2016.       vfprintf (cofp, fmt, args);
  2017. #else
  2018.       _doprnt (fmt, args, cofp);
  2019. #endif
  2020.     }
  2021.  
  2022.   va_end (args);
  2023. }
  2024.  
  2025.  
  2026. /*
  2027.  * The name of the divert file, shared between divert() and undivert()
  2028.  * functions.
  2029.  */
  2030. static char divertfname[512];
  2031.  
  2032. static void
  2033. divert ()
  2034. {
  2035.   char buf[512];
  2036.   char *cp;
  2037.  
  2038.   assert (divertfp == NULL);
  2039.  
  2040.   /* Open divert file. */
  2041.  
  2042.   cp = tmpnam (buf);
  2043.   if (cp == NULL)
  2044.     fatal (_("couldn't create divert file name: %s"), strerror (errno));
  2045.  
  2046.   strcpy (divertfname, cp);
  2047.  
  2048.   divertfp = fopen (divertfname, "w+b");
  2049.   if (divertfp == NULL)
  2050.     fatal (_("couldn't create divert file \"%s\": %s"), divertfname,
  2051.        strerror (errno));
  2052.  
  2053.   if (remove (divertfname) == 0)
  2054.     /* Remove successfull, no need to remove file in undivert(). */
  2055.     divertfname[0] = '\0';
  2056.  
  2057.   cofp = divertfp;
  2058. }
  2059.  
  2060.  
  2061. static void
  2062. undivert ()
  2063. {
  2064.   char buf[1024];
  2065.   int doc_level = 0;
  2066.   char *cp;
  2067.  
  2068.   assert (divertfp != NULL);
  2069.  
  2070.   if (fseek (divertfp, 0, SEEK_SET) != 0)
  2071.     fatal (_("couldn't rewind divert file: %s"), strerror (errno));
  2072.  
  2073.   while (fgets (buf, sizeof (buf), divertfp))
  2074.     {
  2075.       if (strncmp (buf, "%%BeginDocument", 15) == 0)
  2076.     doc_level++;
  2077.       else if (strncmp (buf, "%%EndDocument", 13) == 0)
  2078.     doc_level--;
  2079.  
  2080.       if (doc_level == 0)
  2081.     {
  2082.       if (strncmp (buf, "% User defined strings", 22) == 0)
  2083.         {
  2084.           fputs (buf, ofp);
  2085.           while (fgets (buf, sizeof (buf), divertfp))
  2086.         {
  2087.           if (strncmp (buf, "%%EndPageSetup", 14) == 0)
  2088.             break;
  2089.  
  2090.           /* Patch total pages to the user defined strings. */
  2091.           cp = strchr (buf, '\001');
  2092.           if (cp)
  2093.             {
  2094.               *cp = '\0';
  2095.               fputs (buf, ofp);
  2096.               fprintf (ofp, "%d", total_pages_in_file);
  2097.               fputs (cp + 1, ofp);
  2098.             }
  2099.           else
  2100.             fputs (buf, ofp);
  2101.         }
  2102.         }
  2103.     }
  2104.  
  2105.       fputs (buf, ofp);
  2106.     }
  2107.  
  2108.   fclose (divertfp);
  2109.   divertfp = NULL;
  2110.  
  2111.   /* Do we have to remove the divert file? */
  2112.   if (divertfname[0])
  2113.     (void) remove (divertfname);
  2114.  
  2115.   cofp = ofp;
  2116. }
  2117.